#ifndef WINDOWS_OPENGL_RENDERER_H
#define WINDOWS_OPENGL_RENDERER_H

//OpenGL headers
#include <gl\gl.h>			// Header File For The OpenGL32 Library
#include <gl\glu.h>			// Header File For The GLu32 Library
//#include <gl\glaux.h>		// Header File For The Glaux Library

//Standard library headers
#include <windows.h>
#include <vector>

//Ouden headers
#include <core/IRenderer.h>
#include <utils/Pluginterface.h>
#include <utils/Maths.h>

//The gl header doesn't contain this define from newer versions of OpenGL
#define GL_CLAMP_TO_EDGE 0x812F

//Window callback
LRESULT CALLBACK WndProc(	HWND	hWnd,	      // Handle For This Window
	                        UINT	uMsg,			// Message For This Window
	                        WPARAM	wParam,	// Additional Message Information
	                        LPARAM	lParam);	// Additional Message Information

class WindowsOpenGL2DCamera : public I2DCamera
{
	
public:
	
	//TODO: Implement this class!
	
	virtual void update()
	{
		
	}
	
	virtual void track( const Vector* vPos )
	{
		
	}
	virtual void setPos( Vector vPos )
	{
		
	}
	virtual void moveTo( Vector vPos )
	{
		
	}
	virtual void setZoom( Scalar fValue )
	{
		
	}
	virtual void zoomTo( Scalar fValue )
	{
		
	}
	virtual void setAngle( Scalar fValue )
	{
		
	}
	virtual void rotateTo( Scalar fAngle )
	{
		
	}
};

class WindowsOpenGLRenderer : public IRenderer
{
public:

	//Constructor and destructor
	WindowsOpenGLRenderer(); 
	~WindowsOpenGLRenderer() 		{ if (hWnd) KillWindow(); }
	
	virtual bool init( String sTitle, unsigned iWidth, unsigned iHeight, unsigned iBitsPerPixel, bool bFullscreen )
	{
		CreateRendererWindow( sTitle.c_str(), iWidth, iHeight, iBitsPerPixel, bFullscreen );
		
		m_pCamera = new WindowsOpenGL2DCamera();
	}
	virtual void resize( unsigned iWidth, unsigned iHeight )
	{
		ResizeScene( iWidth, iHeight );
	}
	//Called once per frame (at start)
	virtual bool update()
	{
		//OpenGL error checking
		int r = glGetError();
		if (r != GL_NO_ERROR) { std::cout << "Error in WindowsOpenGLRenderer::update(): OpenGL Error " << r << std::endl; }
		
		return CheckMessages();
	}
	//Called once per frame (at end)
	virtual void renderScene()
	{
		DrawScene();
	}
	virtual unsigned getWidth()
	{
		return width;
	}
   virtual unsigned getHeight()
   {
   	return height;
   }
   
	virtual I2DCamera* getCamera() { return m_pCamera; }
    
    //These are used to alter the transformation matrices before rendering
    virtual void beginTransformation() { glPushMatrix(); }
    virtual void endTransformation()   { glPopMatrix(); }
    virtual void translate( const Vector& vOffset )
    {
		glTranslatef( vOffset[0], vOffset[1], vOffset[2] );
	}
    virtual void rotate( Scalar fAngle, Vector vAxis = Vector(0, 0, 1) )
    {
		glRotatef( fAngle, vAxis[0], vAxis[1], vAxis[2] );
	}
    virtual void scale( const Vector& vScale )
    {
		glScalef( vScale[0], vScale[1], vScale[2] );
	}
    
	//Returns whether the renderer device is active and thus whether the game loop should pause
	virtual bool isActive()
	{
		return active;
	}
	//Returns whether the renderer has received a quit message and thus the game loop should end
	virtual bool isQuit()
	{
		return quit;
	}
	
	//Main functions
	bool 	CreateRendererWindow(const char* Title, int Width, int Height, int Bits, bool Fullscreen);
	void 	ResizeScene(int Width, int Height);
	void 	KillWindow();
	bool 	DrawScene();
	
	void DrawQuad(Vector3D Pos, Vector3D Size, bool Filled = true, Vector3D TexStretch = Vector3D(1, 1, 1));
	
	bool	CheckMessages();
	
	//Variable access functions
	Vector       getViewSize()         { return Vector3D(width, height); }
	HWND 	     getWindowHandle()     { return hWnd; }
	HDC	         gethDC()	           { return hDC; }
	void         setActive(bool value) { active = value; }
	void         setQuit(bool value)   { quit = value; }
	
	virtual void drawGfxModel( const GfxModel& model );
    //TODO: Consider whether these should be replaced or used by a camera class.
    //      Also consider where the camera class would reside.
    //virtual void translateView( const Vector& vOffset ) = 0;
    //virtual void rotateView( Scalar fAngle, const Vector& vAxis ) = 0;
    //virtual void scaleView( const Vector& vScale ) = 0;
	
private:
	
	//General variables
	bool         fullscreen;
	unsigned int width;
	unsigned int height;
	
	I2DCamera* m_pCamera;
	
	//Window variables
	HGLRC	  hRC;	     //Permanent Rendering Context
	HWND	  hWnd;      //Holds Our Window Handle
	HINSTANCE hInstance; //Holds The Instance Of The Application
	HDC       hDC;       //Private GDI Device Context
	bool      active;    //Is the windows active?
	bool      quit;      //Have we received a quit message? 
	
	//Renderer initialisation
	bool 	Init();
};

//Function to find a renderer instance from a given hWnd 
WindowsOpenGLRenderer* getRendererFromWindowHandle(HWND hWnd);

DEFINE_PLUGIN( WindowsOpenGLRenderer, IRenderer )

#endif //WINDOWS_OPENGL_RENDERER_H 
